package submarines;

import java.util.*;
import submarines.ui.*;
import javax.xml.bind.*;
import submarines.thread.TimeoutChecker;

/**
 *
 * @author elad
 */
public class PlayerManager {

    LinkedList<Player> players;
    LinkedList<Player> removed;
    LinkedList<Thread> eaThreads;
    int compNum = 0;
    int maxPlayers = -1;
    boolean xmlLoaded;
    boolean gameBegun;
    //List<SubmarinesBindings.Player> subPlayers = null;   
    //List<SubmarinesBindings.Board> subBoards =null;   

    public PlayerManager() {

        players = new LinkedList<Player>();
        removed = new LinkedList<Player>();
        eaThreads = new LinkedList<Thread>();
        xmlLoaded = false;
        gameBegun = false;
    }

    public PlayerManager(List<SubmarinesBindings.Player> xmlPlayer, List<SubmarinesBindings.Boat> xmlBoats, List<SubmarinesBindings.Board> xmlBoards) throws JAXBException {
        int i, j;
        OnlineEaPlayer tmpEa = null;
        OnlineHumanPlayer tmpHum = null;
        Ui.playerType pType;
        SubmarinesBindings.Board b = null;

        players = new LinkedList<Player>();
        removed = new LinkedList<Player>();
        eaThreads = new LinkedList<Thread>();
        //kicker = new Thread(new TimeoutChecker(this));
        gameBegun = false;
        
        setMaxPlayers(xmlPlayer.size());
        for (i = 0; i < xmlPlayer.size(); i++) {
            for (j = 0; j < xmlBoards.size(); j++) {
                if (xmlBoards.get(j).getPlayer().equals(xmlPlayer.get(i).getName())) {
                    b = xmlBoards.get(j);
                }
            }
            if (b == null) {
                throw new JAXBException("Borad not found for player " + xmlPlayer.get(i).getName());
            } else {
                if (xmlPlayer.get(i).getType().equals("HUMAN")) {
                    tmpHum = new OnlineHumanPlayer(xmlPlayer.get(i).getName(), xmlBoats, b);
                    addPlayer(tmpHum);
                } else if (xmlPlayer.get(i).getType().equals("COMPUTER")) {
                    tmpEa = new OnlineEaPlayer(xmlPlayer.get(i).getName(), xmlBoats, b, this);
                    addPlayer(tmpEa);
                } else {
                    throw new JAXBException("Invalid Player type for player: " + xmlPlayer.get(i).getName());
                }
                //players.add(tmp);
            }
        }
        setCompetitorBoards();
        xmlLoaded = true;
    }
    
    public boolean isXml() {
        return xmlLoaded;
    }

    public void setMaxPlayers(int size)
    {
        this.maxPlayers= size;
    }
    
    public int getMaxPlayers()
    {
        return this.maxPlayers;
    }
    
    public boolean addPlayer(Player p) {
        if (p.getType().equals(Config.COMPUTER_PLAYER)) {
            OnlineEaPlayer op = (OnlineEaPlayer)p;
            Thread thisPlayer = new Thread(op);
            thisPlayer.setName(p.name);
            eaThreads.add(thisPlayer);
        }
        if (players.size() < maxPlayers) {
            players.add(p);
        } else {
            return false;
        }
        return true;
    }
    
    public boolean addPlayerRandom(Player p) {
        Random generator = new Random();
        int num = players.size()+1;
        int ind = generator.nextInt(num) + 0;
            if (players.size() < maxPlayers) {
            players.add(ind, p);
        } else {
            return false;
        }
        return true;
    }
    
    public Player get(int i) {
        return players.get(i);
    }

    /**
     * Function return a random (or not) name for a computer player
     * @return 
     */
    private String getRandomName() {
        compNum++;
        return "Computer " + compNum;
    }

    /**
     * Function return a referance to the next player in turn
     * @return 
     */
    public Player getNextPlayer() {
        Player next = players.removeFirst();
        players.add(next);

        return next;
    }
    
    public void playersTurn()
    {
        Player next = players.removeFirst();
        players.add(next);
    }
    
    public Player thisTurnPlayer() {
        return players.getFirst();
    }

    /**
     * The function takes "player" out of the game;
     * 
     * @param player 
     */
    public void playerQuit(Player player) {
        players.remove(player);
        setMaxPlayers(players.size());
        removed.add(player);
    }

    public void removePlayer(Player player) {
        players.remove(player);
        setMaxPlayers(players.size());
    }
    
    public void reInitiate() {
        int i;
        while (removed.size() > 0) {
            players.add(removed.remove());
        }
        for (i = 0; i < players.size(); i++) {
            players.get(i).reInitiate();
        }
        setCompetitorBoards();
    }

    /**
     * Function returns how many players are still in the game
     * @return 
     */
    public int numPlayersLeft() {
        return players.size();
    }

    public Player getPlayerByName(String name) {
        int i;
        Player p;
        for (i = 0; i < players.size(); i++) {
            p = players.get(i);
            if (p.getName().equals(name)) {
                return p;
            }
        }
        return null;
    }

    public void setCompetitorBoards() {
        Player p;
        int i;
        int j;
        for (j = 0; j < players.size(); j++) {
            p = players.get(j);
            for (i = 0; i < players.size(); i++) {
                if (!players.get(i).getName().equals(p.getName())) {
                    p.addCompetitor(players.get(i));
                }
            }

        }
    }
    
    public String[] getPlayerNames() {
        if (players.size() == 0)
            return null;
        
        String[] names = new String[players.size()];
        int i;
        for (i = 0; i < players.size(); i++) {
            names[i] = new String(players.get(i).getName());
        }
        return names;
    }
    
    public LinkedList<OnlineHumanPlayer> checkUsersSessions()
    {
        LinkedList<OnlineHumanPlayer> noSessionPlayers = new LinkedList<OnlineHumanPlayer>();
        for(int i=0; i<players.size();i++)
        {
            if(players.get(i).pType.equals(Config.HUMAN_PLAYER))
            {
                OnlineHumanPlayer currPlayer = (OnlineHumanPlayer) players.get(i);
                if (currPlayer.getSessionId() == null)
                {
                    noSessionPlayers.add(currPlayer);
                }
            }
        }
        return noSessionPlayers;
    }
          
    public boolean hasGameBegun() {
        return gameBegun;
    }
    
    public void startGame() {
        if (gameBegun)
            return;
        if (checkPlayers()) {
            startEaPlayers();
            gameBegun = true;
        }
    }

    public OnlineHumanPlayer getPlayerBySession(String session) {
        int i;
        OnlineHumanPlayer p;
        Player player;
        if (players.size() == 0)
            return null;
          
        for (i = 0; i < players.size(); i++) {
            player = players.get(i);
            if(player.getType().equals(Config.HUMAN_PLAYER)) {
                p = (OnlineHumanPlayer)players.get(i);
                if (p.getSessionId() != null) {
                    if (p.getSessionId().equals(session)) {
                        return p;
                    }
                }
            }
        }
        return null;
    }
    
    public OnlineHumanPlayer getDeadPlayerByID(String session) {
        int i;
        OnlineHumanPlayer p;
        Player player;
        if (removed.size() == 0)
            return null;
          
        for (i = 0; i < removed.size(); i++) {
            player = removed.get(i);
            if(player.getType().equals(Config.HUMAN_PLAYER)) {
                p = (OnlineHumanPlayer)removed.get(i);
                if (p.getSessionId() != null) {
                    if (p.getSessionId().equals(session)) {
                        return p;
                    }
                }
            }
        }
        return null;
    }
    
    public boolean checkPlayers() {
        OnlineHumanPlayer p;
        Player tmp;
        if(numPlayersLeft() < getMaxPlayers())
            return false;
        for (int i = 0; i < players.size(); i++) {
            tmp = players.get(i);
            if (tmp.pType.equals(Config.HUMAN_PLAYER)) {
                p = (OnlineHumanPlayer)players.get(i);
                if(!p.isReady())
                {
                    return false;
                }
            }
        }
        return true;
    }
    
    public void startEaPlayers() {
        try {
            for (int i = 0; i < eaThreads.size(); i++) {
                Thread t = eaThreads.get(i);
                if (!t.isAlive()) {
                    t.start();
                }
            }
        } catch (IllegalThreadStateException e) {
        }
    }

    public void timeOut() {
        Date now = new Date();
        OnlineHumanPlayer p;
        Player tmp;
        if(players.size() <= 0){
            return;
        }
        tmp = players.get(0);
        if ((tmp.pType.equals(Config.HUMAN_PLAYER)) && (hasGameBegun() || (!hasGameBegun()))) {
            p = (OnlineHumanPlayer)players.get(0);
            if (now.getTime()  - p.last.getTime() >= (Config.TIMEOUT * Config.MS_TO_MIN)) {
                p.setDeathCause(2);
                removePlayer(p);
            }
        }
    }
}
